import re,csv,traceback,tkinter as tk
from tkinter import filedialog,messagebox
# 初始化 创建正则表达式，初始化全局变量
class CompareDataTool:
    parent_role=re.compile(r'-\s([^\n]*?(?=\s*\|))\s*\|\s*(\d+)-(\d+)')
    child_role=re.compile(r'-\s([^\n]*?(?=\s*\|))\s*\|\s*(\d+)\s*\|\s*([\d.]*)')
    sole_file_role=re.compile(r'(\d+)[^\n]*-\s([^\n]*?(?=\s*\|))\s*\|\s*(\d+)\s*\|\s*([\d.]*)')
    file1_path=None
    file2_path=None
    # 启动窗体
    def __init__(self):
        # 创建主窗口  
        self.root = tk.Tk()  
        self.root.title('绘制事件耗时比对')  
        # 设置窗口大小（可选）  
        self.root.geometry('500x250')  
        # 创建一个标签用于显示文件信息  
        self.file1_label = tk.Label(self.root, text='请选择第1份数据')  
        self.file1_label.pack(pady=20)  # pady 是垂直方向上的填充  
        # 创建一个标签用于显示文件信息  
        self.file2_label = tk.Label(self.root, text='请选择第2份数据')  
        self.file2_label.pack(pady=20)  # pady 是垂直方向上的填充  
        # 创建一个按钮，点击时调用 select_file 函数  
        self.select_button = tk.Button(self.root, text='选择第1份数据', command=self.select_file)  
        self.select_button.pack(pady=10)  # pady 是垂直方向上的填充  
        self.get_DateOfBirth_button = tk.Button(self.root, text='绘制事件耗时比较小工具',command=self.get_compare_data)  
        self.get_DateOfBirth_button.pack(pady=10)  # pady 是垂直方向上的填充  
        # 先更新按钮状态设置禁用
        self.update_button_state()  
        # 进入主循环，等待用户操作  
        self.root.mainloop()
    # 动态选择编码读取文件
    def FindLinesByFile(self,file_path)-> list:
        # 尝试使用UTF-8编码打开文件  
        try:  
            with open(file_path, 'r', encoding='utf-8') as file:  
                return file.readlines()  
        except UnicodeDecodeError:  
            # 如果UTF-8编码打开文件失败，尝试使用GBK编码  
            try:  
                with open(file_path, 'r', encoding='gbk') as file:  
                    return file.readlines()  
            except UnicodeDecodeError:  
                # 如果GBK也失败，抛出异常  
                raise ValueError(f'致命错误:文件既不是UTF-8编码也不是GBK编码!!!')  
    # 弹出文件选择对话框 
    def select_file(self)-> None:     
        # askopenfilename 返回选择的文件路径  
        if self.file1_path and self.file2_path:
            self.file1_path=None
            self.file2_path=None
            self.file1_label.config(text='请选择第1份数据')  
            self.file2_label.config(text='请选择第2份数据')  
            self.update_button_state()
        else:
            self.file_path=None
            self.file_path= filedialog.askopenfilename(filetypes=[('TXT文本文件', '*.txt')])  
            # 检查用户是否选择了文件  
            if self.file_path:  
                # 更新标签以显示文件路径  
                if self.file1_path is None:
                    self.file1_path=self.file_path
                    self.file1_label.config(text=f'您选择的文件是: {self.file_path}')  
                    self.select_button.config(text='选择第2份数据')
                else:
                    self.file2_path=self.file_path
                    self.file2_label.config(text=f'您选择的文件是: {self.file_path}')  
                    self.select_button.config(text='重新选择数据')
                # 更新按钮状态
                self.update_button_state()  
    # 更新按钮状态
    def update_button_state(self)->None:  
        # 检查file_path是否为None，并相应地更新按钮状态  
        if self.file1_path and self.file2_path:  
            self.get_DateOfBirth_button.config(state='normal', text='比较绘制事件耗时')  
        if self.file1_path and (not self.file2_path):
            self.get_DateOfBirth_button.config(state='normal', text='统计绘制事件耗时')
        else:  
            self.get_DateOfBirth_button.config(state='disabled', text='比较绘制事件耗时')  
    # 根据文件行获得各项耗时
    def get_data_from_lines_sole_file(self,lines)->dict:
        dict_data={}
        parent_data={}
        for line in lines:
            match=self.sole_file_role.search(line)
            if match:
                id=match.group(1)
                action=int(match.group(3))
                key_parts = [] 
                for i in parent_data:
                    if action >= parent_data[i][0] and action < parent_data[i][1]:
                        key_parts.append(i + '->')  
                key_str = ''.join(key_parts) + match.group(2)
                name=match.group(2)
                duration=match.group(4)
                dict_data[id]={'Name':name,'Duration':float(duration),'Path':key_str}
            else:
                match=self.parent_role.search(line)
                if match:
                    if match.group(1)=="FRAME":
                        parent_data.clear()
                    parent_data[match.group(1)]=(int(match.group(2)),int(match.group(3)))
        return dict_data
    # 根据文件行获取各项数据
    def get_data_from_lines(self,lines)->dict:
        dict_data={}
        parent_data={}
        for line in lines:
                match=self.child_role.search(line)
                if match:
                    action=int(match.group(2))
                    key_parts = [] 
                    for i in parent_data:
                        if action >= parent_data[i][0] and action < parent_data[i][1]:
                            key_parts.append(i + '->')  
                    key_str = ''.join(key_parts) + match.group(1)
                    # 如果层级相同，项名相同，将耗时合并一起计算
                    if key_str in dict_data:
                        dict_data[key_str]['Duration']+=float(match.group(3))
                    else:
                        dict_data[key_str]={'Duration':float(match.group(3))}
                else:
                    match=self.parent_role.search(line)
                    if match:
                        if match.group(1)=="FRAME":
                            parent_data.clear()
                        parent_data[match.group(1)]=(int(match.group(2)),int(match.group(3)))
        return dict_data
    # 比对数据并输出到csv文件
    def compare_and_output_to_csv(self,data_dict1,data_dict2)-> None:
        save_path = filedialog.asksaveasfilename(title="请输入比对结果要保存的文件路径",defaultextension='.csv',  filetypes=[('CSV表格文件', '*.csv')]) 
        diff_data_dict={}
        # 得到耗时差列表
        for key in data_dict2:
                if key in data_dict1:
                    diff_data_dict[key]={
                                            'FirstDuration':round(data_dict1[key]['Duration'],3),
                                            'SecondDuration':round(data_dict2[key]['Duration'],3)}
                    # (data_dict2[key]['Event'],round(data_dict2[key]['Duration']-data_dict1[key]['Duration'],2))
                else:
                    diff_data_dict[key]={
                                            'FirstDuration':'',
                                            'SecondDuration':round(data_dict2[key]['Duration'],3)}
                    # diff_data_dict[key]=(data_dict2[key]['Event'],round(data_dict2[key]['Duration'],2))
        diff_data_dict.update({  
            key: {
                                            'FirstDuration':round(data_dict1[key]['Duration'],3),
                                            'SecondDuration':''}
            for key in data_dict1 if key not in diff_data_dict
            })  
        # 按照耗时从高到低排序
        diff_list = sorted(diff_data_dict.items(),
                            key=lambda item: 
                            ((item[1]['SecondDuration'] if type(item[1]['SecondDuration'])==float else 0)-
                            (item[1]['FirstDuration'] if type(item[1]['FirstDuration'])==float else 0)),
                            reverse=True)  
        with open(save_path, 'w', newline='', encoding='utf-8') as file:
            writer = csv.writer(file)
            writer.writerow(['序号','耗时差','数据1耗时','数据2耗时','绘制事件路径'])
            index=1
            for item in diff_list:
                writer.writerow([
                            index,
                            ((item[1]['SecondDuration'] if type(item[1]['SecondDuration'])==float else 0)-
                            (item[1]['FirstDuration'] if type(item[1]['FirstDuration'])==float else 0)),
                                    item[1]['FirstDuration'],
                                    item[1]['SecondDuration'],
                                    item[0]])
                index+=1
        messagebox.showinfo('比对成功!', f'耗时数据比对结果已保存到：{save_path}')
    # 统计绘制事件耗时
    def get_draw_event_duration(self):
        save_path = filedialog.asksaveasfilename(title="请输入比对结果要保存的文件路径",defaultextension='.csv',  filetypes=[('CSV表格文件', '*.csv')]) 
        lines1 = self.FindLinesByFile(self.file1_path)
        # 录入数据到字典
        data_dict=self.get_data_from_lines_sole_file(lines1)
        if len(data_dict)==0:
            messagebox.showerror('比对失败!',"数据中没有找到预期数据!请检查文件是否正确!")
            return
        # save_path = filedialog.asksaveasfilename(title="请输入比对结果要保存的文件路径",defaultextension='.csv',  filetypes=[('CSV表格文件', '*.csv')]) 
        # 按照耗时从高到低排序
        list1= sorted(data_dict.items(),
                            key=lambda item:item[1]['Duration'],
                            reverse=True)  
        with open(save_path, 'w', newline='', encoding='utf-8') as file:
            writer = csv.writer(file)
            writer.writerow(['Index','EID','Event','Duration','Path'])
            index=1
            for item in list1:
                writer.writerow([
                            index,
                            item[0],
                            item[1]['Name'],
                                    item[1]['Duration'],
                                    item[1]['Path']])
                index+=1
        messagebox.showinfo('统计成功!', f'耗时数据统计结果已保存到：{save_path}')
    # 获取数据比对后的数据
    def get_compare_data(self):
        try:
            if self.file1_path and (not self.file2_path):
                self.get_draw_event_duration()
                return
            lines1 = self.FindLinesByFile(self.file1_path)
            lines2 = self.FindLinesByFile(self.file2_path)
            # 录入数据到字典
            data_dict1=self.get_data_from_lines(lines1)
            data_dict2=self.get_data_from_lines(lines2)
            if len(data_dict1)==0 and len(data_dict2)==0:
                messagebox.showerror('比对失败!',"两份数据中均没有找到预期数据!请检查文件是否正确!")
            elif len(data_dict1)==0:
                messagebox.showerror('比对失败!',"第1份数据中没有找到预期数据!请检查第1份数据文件是否正确!")
            elif len(data_dict2)==0:
                messagebox.showerror('比对失败!',"第2份数据中没有找到预期数据!请检查第2份数据文件是否正确!")
            else:
                self.compare_and_output_to_csv(data_dict1,data_dict2)
        except PermissionError:
            messagebox.showerror('比对失败!',"文件拒绝访问!请检查目标文件是否被其他程序占用!")
        except Exception as e:  
            # 获取当前异常的堆栈跟踪  
            tb = traceback.format_exc()  
            messagebox.showerror('比对失败!', f'{type(e)}类型错误!详情：\n{tb}')
if __name__ == '__main__':
    compareDataTool=CompareDataTool()